package edu.ivanov.apm.chaos;

import java.util.*;

/**
 * Порождает правила для рисования фракталов.
 *
 * User: Илья
 * Date: 09.10.13
 * Time: 21:16
 */
public class RuleProducer {

	private String axiom;
	private Map<Character, String> productions;

	/**
	 * @param axiom Аксиома L-системы.
	 */
	public void setAxiom(String axiom) {
		this.axiom = axiom;
	}

	/**
	 * @return Аксиома L-системы.
	 */
	public String getAxiom() {
		return axiom;
	}

	/**
	 * @param productions Правила порождения новых правил.
	 */
	public void setProductions(Map<Character, String> productions) {
		this.productions = productions;
	}

	/**
	 * @param source Исходное правило.
	 * @return Порождённое правило.
	 */
	public String produce(String source) {
		if (source == null ||
			productions == null ||
			productions.isEmpty())
			return source;

		StringBuilder rule = new StringBuilder(source);
		ArrayList<Replacement> replacements = findReplacements(rule);
		StringBuilder newRule = applyReplacements(rule, replacements);

		return newRule.toString();
	}

	private ArrayList<Replacement> findReplacements(StringBuilder rule) {
		int length = rule.length();
		ArrayList<Replacement> replacements = new ArrayList<Replacement>();
		for (Map.Entry<Character, String> entry : productions.entrySet()) {
			Character character = entry.getKey();
			if (character == null)
				continue;

			String value = entry.getValue();
			if (value == null)
				continue;

			String searchCharacter = character.toString();

			int fromIndex = 0;
			int index;
			while (fromIndex < length && (index = rule.indexOf(searchCharacter, fromIndex)) >= 0) {
				replacements.add(new Replacement(index, value));
				fromIndex = index + 1;
			}
		}

		return replacements;
	}

	private StringBuilder applyReplacements(StringBuilder rule, ArrayList<Replacement> replacements) {
		if (replacements.isEmpty())
			return rule;

		Collections.sort(replacements);
		int length = rule.length();
		StringBuilder newRule = new StringBuilder();

		Iterator<Replacement> iterator = replacements.iterator();
		Replacement current = iterator.next();
		for (int i = 0; i < length; i++)
			if (i == current.index) {
				newRule.append(current.value);

				if (!iterator.hasNext()) {
					newRule.append(rule.substring(i + 1));
					break;
				}

				current = iterator.next();
			} else
				newRule.append(rule.charAt(i));

		return newRule;
	}

	private static class Replacement implements Comparable<Replacement> {

		public int index;
		public String value;

		public Replacement(int index, String value) {
			this.index = index;
			this.value = value;
		}

		public int compareTo(Replacement another) {
			return index - another.index;
		}
	}
}
